/* Copyright (C) 2016-2017 HangZhou Zenzet Technology Co., Ltd.
 * All right reserved

 * File:lktshm.cpp
 * Author:guojianchuan
 * Date:2017-06-29

 */

/* 系统头文件 */
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/shm.h>
#include <pthread.h>

/* SQLRELAY 头文件 */
#include <rudiments/character.h>
#include <rudiments/stringbuffer.h>
#include <rudiments/debugprint.h>

/* 本项目其他模块头文件 */
#include "lktcommon.h"
#include "lkterr.h"
#include "lktstruct.h"
#include "lktcipher.h"

/* 当前模块头文件 */
#include "lktshm.h"

#define  PREFIX_IV_LEN      16
#define  MASTER_KEY_LEN     64
#define  ITERATION_COUNT    5000
#define  PBKDF2_KEY_LEN     32

lktshm :: lktshm (void)
{
    initOK      = false;
    attach      = false;
    datashm     = NULL;
    dbInstance  = NULL;    
}

lktshm::~lktshm (void)
{
    detachSHM ();
}

bool lktshm::decodePv (void)
{
    int ret = ERR_COMMON_SUCCESS;
    
    char *appSecret = datashm->appInfo.appAccessSecret;
    if (0 == strlen(appSecret))
    {
        return false;
    }

    if (0 == strlen(datashm->appInfo.companyKey.pv))
    {
        return false;
    }

    m_pv.clear();
    /* 
        同步下来的pv，是经过AES加密，然后再编码成base64格式的
        所以需要先对其进行base64解码，再经过AES解密,而AES的密钥
        由appSecret经过PBKDF2加密得到
    */

    /* 获取解密pv的AES key和iv */
    unsigned char* aesKey = NULL;
    unsigned char aesIv[PREFIX_IV_LEN +1] = {0};
    unsigned char out[PBKDF2_KEY_LEN + 1] = {0};
    {
        
        const char *pass          = appSecret;
        int passlen               = strlen (pass);
        unsigned char *salt       = (unsigned char*) appSecret;
        int saltlen               = strlen((char*) salt);
        
        ret = LKT_PBKDF2 (appSecret, passlen, salt, saltlen, ITERATION_COUNT, PBKDF2_KEY_LEN, out);
        if (ERR_COMMON_SUCCESS != ret)
        {
            return false;
        }
        
        /* 获取aes 的key和 iv (iv是key的前PREFIX_IV_LEN位) */
        aesKey = out;
        memcpy ((char*) aesIv, (char*) aesKey, PREFIX_IV_LEN);
    }

    /* 解密pv */
    {
        /* 先对pv进行base64解码 */
        unsigned char *base64pv   = (unsigned char*)datashm->appInfo.companyKey.pv;
        int  base64pvlen          = strlen((char*)base64pv);
        
        int bufLen                =  base64pvlen;  //输出长度和输入长度设置成一样，buffer肯定够用
        unsigned char *decpv      = new unsigned char[bufLen];
        if (NULL == decpv)
        {
            return false;
        }
        memset (decpv, 0, bufLen);
        
        int decpvlen = 0;
        if (NULL == LKT_DecodeBase64  (base64pv, base64pvlen, decpv, &decpvlen))
        {
            delete []decpv;
            return false;
        }

        /* 对pv 进行aes解码 */
        bufLen = decpvlen;
        unsigned char *plainPv = new unsigned char[bufLen]; //AES-CFB输入和输出长度一样，所以buffer大小设置成一样
        if (NULL == plainPv)
        {
            delete []decpv;
            return false;
        }
        memset (plainPv, 0, bufLen);
        
        CIPHER_AES_S cipher;
        memset (&cipher, 0, sizeof (cipher));

        snprintf (cipher.algo, sizeof (cipher.algo), "%s", AES_256_CFB);
        cipher.mode     = DECRYPT;
        cipher.input    = decpv;
        cipher.inlen    = decpvlen;
        cipher.key      = aesKey;
        cipher.iv       = aesIv;
        cipher.output   = plainPv;
        
        ret = LKT_AESCrypt (&cipher);
        if (ERR_COMMON_SUCCESS != ret)
        {
            delete []plainPv;
            delete []decpv;
            return false;
        }

        m_pv.write (cipher.output, cipher.outlen);
        
        delete []plainPv;
        delete []decpv;
    }

    return true;
}

bool lktshm::decodeMasterKey (void)
{
    if (0 == m_pv.getSize ())
    {
        return false;
    }
    
    char *masterKey = datashm->appInfo.companyKey.masterKey;
    if (0 == strlen(masterKey))
    {
        return false;
    }    
    
    m_masterKey.clear();
    m_iv.clear();
    
    /* 先对MasterKey进行 base64解码 */
    int decMasterKeyLen = 0;
    int masterKeyLen = strlen(masterKey);
    unsigned char decMasterKey[masterKeyLen];
    memset (decMasterKey, 0, sizeof (decMasterKey));
    
    if (NULL == LKT_DecodeBase64  ((unsigned char*) masterKey, masterKeyLen, decMasterKey, &decMasterKeyLen))
    {
        return false;
    }

    CIPHER_RSA_S cipher;
    memset (&cipher, 0, sizeof (cipher));
    unsigned char output[MASTER_KEY_LEN] = {0};
    
    cipher.pucKey = (unsigned char*) m_pv.getString ();
    cipher.keyLen = m_pv.getSize ();
    cipher.input  = decMasterKey;
    cipher.inlen  = decMasterKeyLen;
    cipher.output = output;
    
    int ret = LKT_RSADecrypt (&cipher);
    if (ERR_COMMON_SUCCESS != ret)
    {
        return false;
    }
    
    m_masterKey.write (cipher.output, cipher.outlen);
    m_iv.write(cipher.output, PREFIX_IV_LEN);
    
    return true;
}

int lktshm::decodeDataKeyIv (unsigned char* key, unsigned char* iv)
{
    int ret = ERR_COMMON_SUCCESS;

    datakey.clear();
    dataiv.clear();

    /* 对key 进行base64 decode */
    int keyLen = 0;
    unsigned char decKey[KEY_MAX_LEN] = {0};
    if (NULL == LKT_DecodeBase64  (key, strlen((char*)key), decKey, &keyLen))
    {
        return ERR_COMMON_FAILED;
    }

    /* 对decode之后的key,再进行解密 */
    CIPHER_AES_S cipher;
    memset (&cipher, 0, sizeof (cipher));
    unsigned char szDataKey[KEY_MAX_LEN] = {0};
    
    snprintf (cipher.algo, sizeof (cipher.algo), "%s", AES_256_CFB);
    cipher.mode     = DECRYPT;
    cipher.input    = decKey;
    cipher.inlen   = keyLen;
    cipher.key      = (unsigned char*) m_masterKey.getString ();
    cipher.iv       = (unsigned char*) m_iv.getString ();
    cipher.output   = szDataKey;

    ret = LKT_AESCrypt (&cipher);
    if (ERR_COMMON_SUCCESS != ret)
    {
        return ret;
    }
    datakey.write (cipher.output, cipher.outlen);    

    /* 对iv 进行base64 decode */
    int ivLen                       = 0;
    unsigned char decIv[IV_MAX_LEN] = {0};
    if (NULL == LKT_DecodeBase64  (iv, strlen((char*) iv), decIv, &ivLen))
    {
        return ERR_COMMON_FAILED;
    }

    /* 对decode之后的iv,再进行解密 */
    unsigned char szDataIV[IV_MAX_LEN] = {0};
    
    cipher.input    = decIv;
    cipher.inlen   = ivLen;
    cipher.output   = szDataIV;
    cipher.outlen   = 0;
    
    ret = LKT_AESCrypt (&cipher);
    if (ERR_COMMON_SUCCESS != ret)
    {
        return ret;
    }
    dataiv.write (cipher.output, cipher.outlen);

    return ERR_COMMON_SUCCESS;    
}

bool lktshm::attachSHM (void)
{
    if (false == attach)
    {
        key_t shmkey = SHM_KEY;
        int shm_id = shmget(shmkey, sizeof(DATA_SHM_S), 0666 | IPC_CREAT);
        if (shm_id == -1)
        {
            stdoutput.printf("shmget failed. %s", strerror(errno));
            return attach;
        }
        
        void *ptr = shmat(shm_id, (void*) 0, 0);
        if ((void*)-1 == ptr)
        {
            stdoutput.printf("shmat failed. %s", strerror(errno));
            return attach;
        }
        
        datashm = (DATA_SHM_S*) ptr;
        
        attach = true;
    }

    return attach;
}

void lktshm::detachSHM (void)
{
    (void) shmdt(datashm);
}

bool lktshm::init(void)
{
    if (true == initOK)
    {
        return true;
    }

    /* 注册共享内存 */
    if (false == isAttachSHM ())
    {
        if (false == attachSHM ())
        {
            return false;
        }
    }

    /* 解密pv */
    if (false == decodePv())
    {
        return false;
    }

    /* 解密masterKey */
    if (false == decodeMasterKey ())
    {
        return false;
    }

    /* 获取appdatakey */
    b64appdatakey.write (datashm->appInfo.appDataKey.key);

    /* 获取appdataiv */
    b64appdataiv.write (datashm->appInfo.appDataKey.iv);

    initOK = true;

    return initOK;
}

bool lktshm::isAttachSHM (void)
{
    return attach;
}

const char* lktshm::getdatakey(void)
{
    return datakey.getString ();
}

const char* lktshm::getdataiv(void)
{
    return dataiv.getString ();
}

const char* lktshm::getb64appdatakey(void)
{
    return b64appdatakey.getString ();
}

const char* lktshm::getb64appdataiv(void)
{
    return b64appdataiv.getString ();
}

int lktshm::readLock (void)
{
    return pthread_rwlock_rdlock (&datashm->rwLock);
}

int lktshm::readUnLock (void)
{
    return pthread_rwlock_unlock (&datashm->rwLock);
}

DATA_SHM_S* lktshm::getshm(void)
{
    return datashm;
}

